import java.util.Random;


public class Fitness {

	static int PROBLEM = 2;//0 = curve-fitting
	                        //1 = cart-centering
                            //2 = normal distribution classification
	
	static double MAX_CART_TIME = 10;
	static double TIME_STEP = 0.2;
	static double THRUST_FORCE = 1;
	static int CART_TRIALS = 10;
	
//	static float MEAN_N1 = 14.6f;
//	static float SD_N1 = 2;
//	static float MEAN_N2 = 3.1f;
//	static float SD_N2 = 4.61f;
//	static float P_JUNK = 0.1f;
//	static float P_N1 = (1-P_JUNK)/2.0f;
//	static float JUNK_MIN = -10.0f;
//	static float JUNK_MAX = 25;
	static int NORM_TRIALS = 30;
	
	static Random rand = new Random();
	
	public static double getFitness(Individual toTest){
		switch(PROBLEM){
		case 0:
			return curveFit(toTest);
		case 1:
			return cartCenter(toTest);
		default:
			return normalFitness(toTest);
		}
	}
	
	
	private static double normalFitness(Individual whatever){
		
		float MEAN_N1 = rand.nextFloat()*20;
		float SD_N1 = rand.nextFloat()*6+0.25f;
		float MEAN_N2 = rand.nextFloat()*20;
		float SD_N2 = rand.nextFloat()*6+0.25f;
		float P_JUNK = rand.nextFloat()/3.5f;
		float P_N1 = (1-P_JUNK)/2.0f;
		float JUNK_MIN = -10.0f;
		float JUNK_MAX = 25;
		
		double source;
		double fitness = 0;
		double genPoint;
		double guess;
		double varray[] = new double[6];
		int counter[] = new int[3];
		counter[0] = 0;
		counter[1] = 0;
		counter[2] = 0;
		varray[0] = MEAN_N1;
		varray[1] = SD_N1;
		varray[2] = MEAN_N2;
		varray[3] = SD_N2;
		varray[4] = P_JUNK;
		
		for(int k=0; k<NORM_TRIALS; k++){
			MEAN_N1 = rand.nextFloat()*20;
			SD_N1 = rand.nextFloat()*6+0.25f;
			MEAN_N2 = rand.nextFloat()*20;
			SD_N2 = rand.nextFloat()*6+0.25f;
			P_JUNK = rand.nextFloat()/3.5f;
			
			varray[0] = MEAN_N1;
			varray[1] = SD_N1;
			varray[2] = MEAN_N2;
			varray[3] = SD_N2;
			varray[4] = P_JUNK;
			
			source = rand.nextDouble();
			if(source<P_JUNK){
				genPoint = rand.nextDouble()*(JUNK_MAX - JUNK_MIN);
				genPoint += JUNK_MIN;
				counter[0]++;
			}else if(source< (P_JUNK + P_N1)){
				genPoint = SD_N1*rand.nextGaussian() + MEAN_N1;
				counter[1]++;
			}else{
				genPoint = SD_N2*rand.nextGaussian() + MEAN_N2;
				counter[2]++;
			}
			varray[5] = genPoint;
			
			guess = whatever.evaluate(varray);
			
			if(guess<1 && guess>-1){
				if(source>=P_JUNK)
					fitness++;
			}else if(guess>=1){
				if(source<P_JUNK || source>=(P_JUNK + P_N1))
					fitness++;
			}else{
				if(source < (P_JUNK + P_N1))
					fitness++;
			}
			
		}
		//System.out.println("Junk ("+counter[0]+"), N1 ("+counter[1]+"), N2 ("+counter[2]+")");
		return fitness;
	}
	
	private static double cartCenter(Individual blindEngineer){
		int thrust = 1;
		double returner = 0;
		double temp = 0.0;
		double pos;
		double vel;
		double varray[] = new double[2];
		double time;
		
		for(int y=0; y<CART_TRIALS; y++){
			pos = rand.nextDouble()*1.5 - 0.75;
			vel = rand.nextDouble()*1.5 - 0.75;
			time = 0.0;
			while(time<MAX_CART_TIME && (Math.abs(vel)>0.01 || Math.abs(pos)>0.01)){
				varray[0] = pos;
				varray[1] = vel;
				temp = blindEngineer.evaluate(varray);
				if(temp==0){
					thrust = 1;
				}else{
					thrust = (temp<0)?-1:1;
				}
				
				vel = vel + TIME_STEP*THRUST_FORCE*thrust;
				pos = pos + TIME_STEP*vel;
				
				time += TIME_STEP;
			}
			
			returner += time;
		}
		return returner;
	}
	
	private static double curveFit(Individual curve){
		double fitness = 0;
		double real = 0;
		double x[] = new double[1];
		for(int k = 0; k<21; k++){
			x[0] = k-10;
//			real = Math.PI/2.0 - x[0] - (Math.pow(x[0], 3)/6);
//			real = 8.69*x[0] + 2;
			real = 2.8*x[0]*x[0]-5*x[0]+9.0;
			double temp = curve.evaluate(x);
			fitness += (real - temp) * (real - temp);
		}
		
		return Math.sqrt(fitness)*(Math.sqrt(Math.sqrt(curve.root.getSize())));
		//return Math.sqrt(fitness)*((Math.sqrt(curve.root.getSize())));
	}
}
